/**
 * 
 */
package com.ms.service.module.zone;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ms.game.bank.BankProcessor;
import com.ms.game.bank.message.BankRequest;
import com.ms.game.bank.message.BankResponse;
import com.ms.game.common.GameType;
import com.ms.game.common.container.GameLimitInfo;
import com.ms.game.common.utility.SimpleTimer;
import com.ms.game.db.dto.DataTransfer;
import com.ms.game.gmtool.PlayerDisableData;
import com.ms.game.gmtool.message.CAppleIapAskCashRequest;
import com.ms.game.gmtool.message.CAppleIapVerifyRequest;
import com.ms.game.gmtool.message.CApplyIAPOrderIDRequest;
import com.ms.game.gmtool.message.CApplyIAPVerifyRequest;
import com.ms.game.invitefriend.bin.ItemInfo;
import com.ms.game.itembox.ChangeItemLister;
import com.ms.game.itembox.Item;
import com.ms.game.itembox.ItemChangeParam;
import com.ms.game.itembox.ItemChangeWay;
import com.ms.game.itembox.ItemSimpleData;
import com.ms.game.itembox.container.ItemBasic;
import com.ms.game.itembox.container.ItemChannelBroadcastCard;
import com.ms.game.itembox.db.ItemRefineDCLAsync;
import com.ms.game.itembox.db.RollItemInGameLogModel;
import com.ms.game.itembox.message.CUseBroadcastCardRequest;
import com.ms.game.itembox.message.SItemRefineResult;
import com.ms.game.itembox.message.SSimpleItem;
import com.ms.game.itembox.message.SZoneGetItemListResult;
import com.ms.game.lobby.db.UpdateDisconnectInfoAsync;
import com.ms.game.lobby.db.UpdateLastLoginTimeAsync;
import com.ms.game.lobby.message.ContinuiusRewardInfo;
import com.ms.game.login.bin.ContinuousLoginReward;
import com.ms.game.login.bin.ContinuousLoginRewardBin;
import com.ms.game.login.bin.ContinuousLoginRewardDetail;
import com.ms.game.offlineSpin.OfflineSpinResultData;
import com.ms.game.offlineSpin.OfflineSpinStopCondition;
import com.ms.game.offlineSpin.async.SaveOfflineSpinResultAsync;
import com.ms.game.offlineSpin.async.SaveOfflineSpinStopResultAsync;
import com.ms.game.purchase.bin.PurchaseData;
import com.ms.game.purchase.bin.PurchaseItemInfo;
import com.ms.game.purchase.bin.PurchaseItemInfoIOS;
import com.ms.game.purchase.message.SPurchaseDataListResult;
import com.ms.game.purchase.message.SPurchaseDataListResult_IOS;
import com.ms.game.slot.base.GainRateSystem;
import com.ms.game.slot.base.SlotBetInfo;
import com.ms.player.Player;
import com.ms.player.PlayerAttributeType;
import com.ms.player.PlayerNickNameParser;
import com.ms.player.PlayerSwitchList;
import com.ms.player.PlayerUserType;
import com.ms.player.UpdateAttribute;
import com.ms.service.error.ErrorCode;
import com.ms.service.module.account.db.AccountDAO;
import com.ms.service.module.account.db.CooperatorInfoField;
import com.ms.service.module.account.message.SCharacterLoginNotify;
import com.ms.service.module.account.message.SSessionCloseNotify;
import com.ms.service.module.analysisgroup.AnalysisGroupProcessor;
import com.ms.service.module.analysisgroup.CoinExpenditureWay;
import com.ms.service.module.broadcastlistener.LoginLogoutListener;
import com.ms.service.module.commonsettings.CommonSendItemSetting;
import com.ms.service.module.commonsettings.CommonSettings;
import com.ms.service.module.commonsettings.DailyLoginSendSettings;
import com.ms.service.module.commonsettings.DailySendItemInfo;
import com.ms.service.module.datacenterlite.CentralAsyncCommand;
import com.ms.service.module.processer.BasiceServiceProcesser;
import com.ms.service.module.zone.db.LoadCashGoldTransactionDetailsDCLAsync;
import com.ms.service.module.zone.db.LoadPlayerCashItemDCLAsync;
import com.ms.service.module.zone.message.CCashGoldTransactionDetailsRequest;
import com.ms.service.module.zone.message.CPlayerLoginZoneRequest;
import com.ms.service.module.zone.message.CPlayerSwitchZoneRequest;
import com.ms.service.module.zone.message.CTmpCashTakeRequest;
import com.ms.service.module.zone.message.DailyLoginSendResponse;
import com.ms.service.module.zone.message.SCashGoldTransactionDetailsResponse;
import com.ms.service.module.zone.message.SClientReportRequest;
import com.ms.service.module.zone.message.SPlayerLoginZoneResult;
import com.ms.service.module.zone.message.SPlayerSwitchZoneResponse;
import com.ms.service.module.zone.message.SRechargePageResponse;
import com.ms.service.module.zone.message.SRobotAddCashMessage;
import com.ms.service.module.zone.message.SSimplePlayer;
import com.ms.service.module.zone.message.STmpCashTakeResult;
import com.ms.service.module.zone.module.BasicZoneModule;
import com.xpec.c4.db.basic.BasicLogDAO;
import com.xpec.c4.db.dao.PlayerDAO;
import com.xpec.c4.game.character.PeriodSyncStatisInfo;
import com.xpec.c4.game.common.Helper;
import com.xpec.c4.game.common.LoginVerifyCodeManager;
import com.xpec.c4.game.common.LoginVerifyInfo;
import com.xpec.c4.game.datamanager.BaseDataInfo;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoinLister;
import com.xpec.c4.game.datamanager.agent.money.CoinType;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeParam;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeWay;
import com.xpec.c4.game.login.CharacterLoginProcLogic;
import com.xpec.c4.game.login.LoginModule;
import com.xpec.c4.game.time.TimeConstants;
import com.xpec.c4.game.time.TimeSys;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GUIDFactory;
import com.xpec.c4.game.utility.GUIDType;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.ConstantCode;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.contexts.Contexts;
import com.xpec.c4.service.group.transportlayer.GroupMessage;
import com.xpec.c4.service.h5server.H5ServerOperations;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.message.NetMessage;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ModuleName;
import com.xpec.c4.service.module.PropertyKey;
import com.xpec.c4.service.module.ServerProperties;
import com.xpec.c4.service.module.common.ItemInfoManager;
import com.xpec.c4.service.module.common.PlayerCommonInfoManager;
import com.xpec.c4.service.module.datacenter.player.UpdateH5PlayerMoneyToDbAsync;
import com.xpec.c4.service.module.datacenter.player.db.TmpCashChangeWay;
import com.xpec.c4.service.server.CloseType;
import com.xpec.c4.service.server.ServerOperations;

/**
 * 基本的 zone 只負責管理玩家登入登出 ... 等等的基本功能
 * 
 * @author TW15010
 * 
 */
public class BasicZone extends BasiceServiceProcesser {
	private static final Log log = LogFactory.getLog(BasicZone.class);
	
	/** 定時檢查過期登入驗證碼的時間間隔 */
	private final static long CHECK_LOGIN_VERIFY_EXPIRE_INTERVAL = 10 * TimeConstants.MILLI_SECOND_PRE_MINUTE;

	/** zone id (ex: xxxx_n) */
	private String zoneId;

	/** 擁有這個 zone 的 module */
	private BasicZoneModule ownerModule;

	/** 處理檢查過期登入驗證碼的計時器 */
	private SimpleTimer checkExpireVerifyInfoTimer = new SimpleTimer();

	/**
	 * 正在登入 zone 中的玩家清單<br>
	 * 登入完成取到資料後會從這邊清除，並加到玩家清單中<br>
	 * key: player guid
	 */
	private HashMap<GUID, LoginPlayerInfo> loginPlayerList = new HashMap<GUID, LoginPlayerInfo>();

	/**
	 * zone 內的所有玩家清單<br>
	 * key: player guid
	 */
	private HashMap<GUID, Player> playerList = new HashMap<GUID, Player>();
	
	/**
	 * 是否在玩家斷後自動刪除角色資料
	 */
	private boolean autoRemovePlayerOnDissconnect = true;
	
	/**
	 * 遊戲中玩家狀態清單<br>
	 * 只能在主執行緒存取 !!
	 * key: player guid<br>
	 * value: block type {@link BlockType}
	 */
	private HashMap<String, HashMap<String, Integer>> blockListMap = new HashMap<String, HashMap<String,Integer>>();
	
	
	//連續登錄獎勵跨日依據時間(24小時制)
	public static final int CONTINUE_REWARD_CROSS_DAY_REGULATE = 9;
	
	/**
	 * zone 啟動
	 */
	public void onStartup() {
		// 啟動清除過期登入驗證碼的計時器
		checkExpireVerifyInfoTimer
				.startByRemainTime(CHECK_LOGIN_VERIFY_EXPIRE_INTERVAL);
	}

	/**
	 * 收到玩家要求角色登入 zone 的要求
	 * 
	 * @param request
	 */
	public void onPlayerLoginZoneRequest(CPlayerLoginZoneRequest request,
			GUID sessionID) {
		
		DebugLog("<<onPlayerLoginZoneRequest>> " + request.getUserId());
		
		// 角色已經存在 zone 中
		if (playerList.containsKey(request.getUserId()))
		{
			// 踢掉前一個
			Player player = getPlayer(request.getUserId());
			kickPlayer(player, CloseType.KICK_BY_RELOGIN);
		}

		// 檢查驗證碼
		LoginVerifyInfo vinfo = LoginVerifyCodeManager.getInstance()
				.verifyCode(request.getUserId(), getZoneId(),
						request.getVerifyCode());

		// verifyCode 驗證失敗
		if (vinfo == null) {
			sendPlayerLoginZoneResult(sessionID, ErrorCode.LOGIN_ZONE_VERIFY_INFO_ERROR, null);
			return;
		}
		if(log.isInfoEnabled()){
			log.info(LoginModule.LOGIN_TRACE_LOG_STRING+" verifyCode PASS, g: "+request.getUserId().toString());
		}

		// 角色已經存在登入中清單，駁回
		LoginPlayerInfo loginInfo = getLoginPlayerList().get(request.getUserId());
		if (loginInfo != null) {
			if (!loginInfo.isExpire()) {
				sendPlayerLoginZoneResult(sessionID, ErrorCode.LOGIN_ZONE_PLAYER_STILL_LOGIN_ZONE, null);
				return;
			}else{
				if (log.isErrorEnabled()) {
					log.error("player " + request.getUserId()
							+ " is still in LoginPlayerList, but expired, allow continue login zone ...");
				}
			}
		}

		// 將角色 id 加到登入中清單
		getLoginPlayerList().put(request.getUserId(),
				new LoginPlayerInfo(sessionID));
		if (log.isInfoEnabled()) {
			log.info(LoginModule.LOGIN_TRACE_LOG_STRING+" "+GameUtility.getCurMethodName() + ": add player guid " + request.getUserId()+ " to LoginPlayerList");
		}

		// 向 dc 取得玩家資料
		LoginZoneGetPlayerDataFromDCParam param = new LoginZoneGetPlayerDataFromDCParam();
		param.setBasicZone(this);
		param.setSessionId(sessionID);
		
		// 異步載入玩家資料
		// TODO kevin jb async
		LoginZoneGetPlayerAsync async = new LoginZoneGetPlayerAsync();
		async.initial(request.getUserId(), sessionID);
		async.setParam(param);
		
		getOwnerModule().addAsyncCommand(async);
	}

	/**
	 * 登入時向 dc 取玩家資料，玩家資料被傳回時
	 * 
	 * @param playerId
	 *            向 dc 要求資料的 player id
	 * @param player
	 *            從 dc 取回的 player data
	 * @param sessionId
	 *            角色的 session id
	 */
	public void onLoginZoneGetPlayerDataFromDC(GUID playerId, Player player, GUID sessionId) {
		if (playerId == null) {
			sendPlayerLoginZoneResult(sessionId, ErrorCode.LOGIN_ZONE_PLAYER_DATA_NOT_FOUND, null);
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + "() load player fail, player id = null");
			}
			return;
		}

		// 將角色 id 從登入清單中移除
		// call 到這邊就先移除，避免一次意外 load 不到角色資料角色就會永遠被卡住登不進這個 zone
		removeGuidFromLoginPlayerList(playerId);

		// 沒取到角色資料
		if (player == null) 
		{
			sendPlayerLoginZoneResult(sessionId, ErrorCode.LOGIN_ZONE_PLAYER_DATA_NOT_FOUND, null);
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + "() load player fail, player data not found");
			}
			return;
		}

		// 重置屬性更新清單
		player.clearUpdatedAttrList();

		// 填入 session id
		player.setSessionID(sessionId);

		// 填入 zone id
		player.setZoneId(getZoneId());
		
		// 如果角色從h5登入則設定h5Status
		if (ServerProperties.isTargetModule(PlayerCommonInfoManager.getInstance().getGatewayBySessionId(sessionId), ModuleName.H5GATEWAY)) {
			player.setH5Status(true);
		}

		// 更新 common module 資料
		PlayerCommonInfoManager.sendUpdatePlayerSessionInfo(player);
		
//		PlayerCommonInfoManager.getInstance().updatePlayerCommonInfo(player.getAccountId(),
//				playerId, sessionId, player.getNameWithNumber());

		PlayerCommonInfoManager.sendUpdatePlayerZoneID(player, getZoneId());
//		PlayerCommonInfoManager.getInstance().updatePlayerZoneId(playerId, getZoneId());
		//PlayerCommonInfoManager.sendUpdatePlayerChatData(player);
		
/*
		// 計算連續跨日登入次數
		long curTime = TimeSys.currentTimeMills();
		
		
		//設定登入時間
		player.setLoginTime(curTime);
*/	
		// 加入線上玩家清單中
		playerList.put(player.getGuid(), player);
		if (log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(player.getUserType()))
		{
			log.info("add player " + player + " on " + getOwnerModule().getModuleName() + " count="
					+ playerList.size());
		}

		// 角色身上加上心跳消息處理物件
		player.setSyncStatisInfo(new PeriodSyncStatisInfo());

		// 要求 client 開始傳送心跳消息
		PeriodSyncStatisInfo.sendPeriodSyncMessage(sessionId);

		// 傳送登入結果相關資訊給 client
		sendPlayerLoginZoneResult(player.getSessionID(), ErrorCode.SUCCESS, player);
		if(log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(player.getUserType())){
			log.info(LoginModule.LOGIN_TRACE_LOG_STRING+" send login success to client, s: "+player.getSessionID().toString()+"/ g: "+player.getGuid().toString());
		}

		// 角色資料存在於 zone 中，算是登入完成了
		onPlayerLogin(player);

		// 觸發角色身上的登入事件
		player.onLoginZone(zoneId);
	}

	/**
	 * 角色登入完畢進入 zone 的事件
	 * 
	 * @param player
	 */
	public void onPlayerLogin(Player player) {
		removeBlockList(player.getGuid());
		
		LoginLogoutListener.addLogOutPlayerListener(ownerModule, player.getGuid());
	}
	
	/**
	 * 只有在登入時會執行一次
	 */
	public static void onPlayerLoginOnly(BasicServiceModule module, Player player, GUID sessionId){
		String platform = PlayerCommonInfoManager.getInstance().getLoginPlatformBySessionId(sessionId);
		//更新玩家最後登入時間與IP
		UpdateLastLoginTimeAsync saveData = new UpdateLastLoginTimeAsync();		
		saveData.key = player.getGuid();
		saveData.setPlayer(player);
		saveData.setPlatform(platform);
		saveData.setCurrentTime(GameUtility.longToDate(TimeSys.currentTimeMills()));
		saveData.setPlayerIP(PlayerCommonInfoManager.getInstance().getClientIpBySessionId(sessionId));
		module.addAsyncCommand(saveData);

		player.setLastLoginPlatform(platform);
		
		//如果DB的玩家控制欄位有內容
		if(player.getPlayerSwitchList() != null){
			PlayerSwitchList playerSwitchList = player.getPlayerSwitchListClass();
			//各功能判斷是否有被寫入過，因為這欄位不會有初始化，有GMTOOL指令要寫控制才會加入，所以先判斷有沒有東西
			if(playerSwitchList.getClientReport() != null){
				//判斷是否在客戶端回報紀錄的時間之內
				if(playerSwitchList.getClientReport().isReporting()){
					//送訊息給C端，請C端開始執行Client Report
					SClientReportRequest request = new SClientReportRequest();
					request.setIsbegin(true);
					long endTime = player.getPlayerSwitchListClass().getClientReport().getEndTime().getTime();
					request.setEndTime(endTime);
//等CLIENT接			MessageAction.sendMessage(sessionId, MessageTypes.S_CLIENT_REPORT_COMMAND, cmd);
				}
			}
		}
		
		//通知所有 module 角色登入了
		CharacterLoginProcLogic.broadcastCharacterLoginNotify(player, module);
	}

	/**
	 * 定時觸發 update 事件
	 * 
	 * @param difftime
	 *            距離上次觸發經過的時間
	 */
	public void onUpdate(long difftime) {
		// 移除過期的登入驗證碼資料
		if (checkExpireVerifyInfoTimer.update()) {
			LoginVerifyCodeManager.getInstance().removeExpireVerifyCode(getOwnerModule().getModuleName());
			checkExpireVerifyInfoTimer.restart();
		}

		// 更新所有的 player
		for (Map.Entry<GUID, Player> entry : playerList.entrySet()) {
			Player player = entry.getValue();

			// 檢查定期 sync 動作是否正常
			playerPeriodSyncCheck(player, difftime);
			
			// 呼叫 player 本身的 update 事件
			player.onUpdate(difftime);
		}
	}
	
	/**
	 * 檢查定期 sync 動作是否正常, 不正常則踢掉玩家
	 * 
	 * @param player
	 *            玩家資料
	 * @param diffTime
	 *            距離上次呼叫經過的時間
	 */
	public void playerPeriodSyncCheck(Player player, long diffTime) {
		if (player == null) {
			return;
		}

		PeriodSyncStatisInfo syncStatisInfo = player.getSyncStatisInfo();

		// 檢查是否太久沒送
		if (syncStatisInfo != null && !syncStatisInfo.update(diffTime)) {
			// 如果需要踢掉玩家寫在這裡
			// log.error("kick by no send period sync player:" + player);
			// kickPlayer(player, CloseType.KICK_BY_ILEGAL);
		}
	}
	
	/**
	 * 踢掉玩家，強迫玩家斷線時使用
	 * 
	 * @param player
	 *            player data
	 * @param closeType
	 *            kick reason
	 * @see com.xpec.c4.service.server.CloseType
	 */
	public void kickPlayer(Player player, CloseType closeType) {
		if (player == null) {
			return;
		}

		// 強制斷線
		GUID sessionId = player.getSessionID();
		if (ServerProperties.isTargetModule(PlayerCommonInfoManager.getInstance().getGatewayBySessionId(sessionId), ModuleName.H5GATEWAY)) {
			H5ServerOperations.sessionClose(player.getSessionID(), closeType, ownerModule);
		} else {
			ServerOperations.sessionClose(player.getSessionID(), closeType, ownerModule);
		}

		// 將玩家資料從 zone 中移除
		removePlayerDataFromZone(player.getGuid(), false);
	}
	
	/**
	 * 將玩家資料從 zone 中移除
	 * 
	 * @param playerGuid
	 *            player guid
	 */
	public Player removePlayerDataFromZone(GUID playerGuid, boolean isFromSessionCloseNotify) {
		if (playerGuid == null) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + ": playerGuid=null");
			}
			return null;
		}

		if (!playerList.containsKey(playerGuid)) {
			return null;
		}

		Player player = playerList.remove(playerGuid);
		if (player != null) {
			if (log.isInfoEnabled()) {
				log.info("remove player " + player + " on " + getOwnerModule().getModuleName() + " count="
						+ playerList.size());
			}

			//如果不是因為斷線通知而移除的才需要把收聽module移除
			if(!isFromSessionCloseNotify){
				LoginLogoutListener.removeLogOutPlayerListener(ownerModule, playerGuid);
			}
		}

		return player;
	}

	/**
	 * 傳送登入 zone 失敗原因
	 * 
	 * @param sessionID
	 *            client session id
	 * @param result
	 *            結果
	 * @param player
	 *            角色資料
	 * @see com.ms.service.module.zone.constant.LoginZoneResult
	 */
	public void sendPlayerLoginZoneResult(GUID sessionID, int result,
			Player player) {
		if (result != ErrorCode.SUCCESS) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + ": fail, "
						+ ConstantCode.getName(ErrorCode.class, result));
			}
		}

		// 轉成傳給 client 的資料
		SSimplePlayer simplePlayer = DataTransfer.PlayerToSimple(player);

		// 填入資料
		SPlayerLoginZoneResult loginZoneResult = new SPlayerLoginZoneResult();
		loginZoneResult.setResult(result);
		loginZoneResult.setServerTime(TimeSys.currentTimeMills());
		loginZoneResult.setPlayer(simplePlayer);

		// 傳
		NetMessage msg = new NetMessage();
		msg.setMessageType(MessageTypes.S_PLAYER_LOGIN_ZONE_RESULT);
		msg.setMessageContent(loginZoneResult);
		msg.setSessionID(sessionID);
		MessageAction.sendMessage(msg);
	}
	
	/**
	 * 用 player guid 取得指定的 player 資料
	 * 
	 * @param playerGuid
	 *            player guid
	 * @return player data
	 */
	public Player getPlayer(GUID playerGuid) {
		Player player = playerList.get(playerGuid);
		return player;
	}

	public HashMap<GUID, Player> getPlayerList() {
		return playerList;
	}

	public void setPlayerList(HashMap<GUID, Player> playerList) {
		this.playerList = playerList;
	}

	public HashMap<GUID, LoginPlayerInfo> getLoginPlayerList() {
		return loginPlayerList;
	}

	public void setLoginPlayerList(
			HashMap<GUID, LoginPlayerInfo> loginPlayerList) {
		this.loginPlayerList = loginPlayerList;
	}

	public BasicZoneModule getOwnerModule() {
		return ownerModule;
	}

	public void setOwnerModule(BasicZoneModule ownerModule) {
		this.ownerModule = ownerModule;
	}

	public String getZoneId() {
		return zoneId;
	}

	public void setZoneId(String zoneId) {
		this.zoneId = zoneId;
	}
	
	/**
	 * 收到玩家換 zone 的要求
	 * 
	 * @param request
	 */
	public void onPlayerSwitchZoneRequest(CPlayerSwitchZoneRequest request , GUID sessionID , GUID pGUID)
	{
		//傳入玩家連線資訊
		request.setUserId(pGUID);
		
		DebugLog(this.getZoneId());
		// 傳
		GroupMessage msg = new GroupMessage();
		
		msg.setMessageType(MessageTypes.S_ZONE_SWITCH_ZONE_REQUEST);
		msg.setMessageContent(request);
		msg.setSessionID(sessionID);
		msg.setDestModuleID(request.getTargetZoneId());
		msg.setSrcService(this.zoneId);
		
		MessageAction.sendGroupMessage(msg);
	}
	
	/**
	 * 收到zone 要求新zone的請求 
	 * 
	 * @param request
	 */
	public void onZoneSwitchZoneRequest(CPlayerSwitchZoneRequest request ,String srcMoudleID)
	{
		DebugLog("<<onZoneSwitchZoneRequest>>" + this.getZoneId());
		DebugLog("<<onZoneSwitchZoneRequest>>" + this.playerList.size());
		
		
		LoginVerifyInfo vinfo = LoginVerifyCodeManager.getInstance().createVerifyCode(request.getUserId(), request.getTargetZoneId(), LoginVerifyCodeManager.CREATE_VERIFYCODE_PUT_LOCAL);
		
		request.setVerifyCode(vinfo.getVerifyCode());
		request.setErrorCode(ErrorCode.SUCCESS);
		
		// 傳
		GroupMessage msg = new GroupMessage();
		
		msg.setMessageType(MessageTypes.S_ZONE_SWITCH_VERIFYCODE_BACK);
		msg.setMessageContent(request);
		msg.setSessionID(request.getUserId());
		msg.setDestModuleID(srcMoudleID);
		
		MessageAction.sendGroupMessage(msg);
	}

	/**
	 * 收到新zone回傳verifycode消息給client
	 * @param request
	 */
	public void onZoneSwitchVerifyCodeRequest(CPlayerSwitchZoneRequest request , GUID sessionID)
	{
		SPlayerSwitchZoneResponse response = new SPlayerSwitchZoneResponse();
		
		DebugLog("<<onZoneSwitchVerifyCodeRequest>> " + this.getZoneId());
		//移除本身的player data
		if(request.getUserId()!=null)
		{
			DebugLog(" Romove Player Data!!");
			removePlayerDataFromZone(request.getUserId(), false);
		}
		
		response.setVerifyCode(request.getVerifyCode());
		response.setErrorCode(ErrorCode.SUCCESS);
		MessageAction.sendMessage(sessionID, MessageTypes.S_ZONE_SWITCH_VERIFYCODE_RESPONSE, response);
	}

	/**
	 * 處理斷線通知
	 * 
	 * @param notify
	 *            斷線通知內容
	 */
	public void handlePlayerDisconnectNotify(SSessionCloseNotify notify) {
		// 觸發斷線事件
		onPlayerDisconnect(notify);
		
		if (isAutoRemovePlayerOnDissconnect()) 
		{
			if (log.isDebugEnabled()) {
				log.debug("<<handlePlayerDisconnectNotify>> remove Player s:" + notify.getSessionId());
			}
			removePlayerDataFromZone(notify.getPlayerId(), true);
		}
	}
	
	/**
	 * 玩家斷線事件<br>
	 * 如果需要對玩家斷線事件做處理可以改寫這個<br>
	 * 改寫的 function 裡面要呼叫 super.{@link #onPlayerDisconnect(SSessionCloseNotify)}<br>
	 * 這個 function 結束後才會把玩家資料從 zone 中清除。
	 * 
	 * @param notify
	 *            斷線通知內容
	 */
	public void onPlayerDisconnect(SSessionCloseNotify notify) {
		
		
		if (playerList.containsKey(notify.getPlayerId())) {
			//判斷玩家是否在zone上，是在做離線處理

			Player player = getPlayer(notify.getPlayerId());

			UpdateDisconnectInfoAsync updataData = new UpdateDisconnectInfoAsync();
			updataData.playerID = notify.getPlayerId();
			updataData.sessionId = notify.getSessionId();
			updataData.playerName = notify.getPlayerName();
			updataData.logoutTime = TimeSys.currentTimeMills();
			updataData.moduleServer=Contexts.getModule();
			updataData.userType = getPlayer(notify.getPlayerId()).getUserType();
			updataData.key = notify.getPlayerId();
			updataData.venderId = player.getVenderId();
			updataData.subVenderId = player.getSubVenderId();
			
			Contexts.getModule().addAsyncCommand(updataData);
		}
	}
	
	/**
	 * 送出到同地方執行，在callback回來
	 * @param obj
	 * @return
	 */
	public boolean addCentralAsyncCommand(CentralAsyncCommand obj, Object... userData){
		return ownerModule.addCentralAsyncCommand(obj, userData);
	}
	
	/**
	 * 收到 client 使用全頻卡的要求
	 * 
	 * @param player
	 *            player data
	 * @param request
	 *            要求內容
	 */
	public void onUseBroadcastCardRequest(Player player, CUseBroadcastCardRequest request) {
		if (player == null) {
			return;
		}

		BasicZoneModule module = (BasicZoneModule)Contexts.getModule();
		if(module.isStopTransaction()){						
			ItemChannelBroadcastCard.sendUseBroadcastCardResponse(player.getSessionID(),
					ErrorCode.GAME_SERVER_WAIT_TO_CLOSE);
			return;					
		}						
		
		int limitCode = GameLimitInfo.checkLimitByClassify(GameLimitInfo.ITEM_USE, player.getVip(),player.getLevel(), player.isVerifiedMember(), player.isTrialAccount(), player.getUplineCode());
		if(limitCode != GameLimitInfo.TRUE){
			int errorCode = GameLimitInfo.limitToErrorCode(limitCode);
			ItemChannelBroadcastCard.sendUseBroadcastCardResponse(player.getSessionID(), errorCode);
			return;
		}	
		
		
		Item itemOnPlayer = player.getItemBox().getItem(request.getCardSn());
		if (itemOnPlayer == null) {
			ItemChannelBroadcastCard.sendUseBroadcastCardResponse(player.getSessionID(),
					ErrorCode.USE_ITEM_ITEM_NOT_FOUND_FROM_PLAYER);
			return;
		}

		// 道具在數據中找不到
		ItemBasic item = ItemInfoManager.getInstance().getItem(itemOnPlayer.getItemId());
		if (item == null) {
			ItemChannelBroadcastCard.sendUseBroadcastCardResponse(player.getSessionID(),
					ErrorCode.USE_ITEM_ITEM_NOT_FOUND_FROM_BIN);
			return;
		}
		
		if (!item.isOpen()) { //是否開放
			ItemChannelBroadcastCard.sendUseBroadcastCardResponse(player.getSessionID(),
					ErrorCode.ITEM_NOT_OPEN_PLEASE_WAIT);
			return;
		}
		
		// 道具不是全頻卡
		if (!item.isBroadcastCard()) {
			ItemChannelBroadcastCard.sendUseBroadcastCardResponse(player.getSessionID(),
					ErrorCode.USE_ITEM_ITEM_IS_NOT_BROADCAST_CARD);
			return;
		}

		// message 是空的
		if (!GameUtility.isHaveString(request.getMessage())) {
			ItemChannelBroadcastCard.sendUseBroadcastCardResponse(player.getSessionID(),
					ErrorCode.USE_ITEM_BROADCAST_MESSAGE_IS_EMPTY);
			return;
		}
		
		if(isBlock(player.getGuid(), null)){
			ItemChannelBroadcastCard.sendUseBroadcastCardResponse(player.getSessionID(),
					ErrorCode.CANT_USE_ITEM_NOW);
			return;
		}
		
		//加到 block list
		putBlockList(player.getGuid(), null , BlockType.USE_ITEM);

		// order id
		GUID orderId = GUIDFactory.createUUID(GUIDType.TYPE_USE_ITEM);

		// item lister
		ChangeItemLister lister = new ChangeItemLister();
		lister.addItem(request.getCardSn(), null, 1, 0);

		// 先到 dc 去扣除道具
		getOwnerModule().getPlayerDataAgent().updatePlayerItemToDC(orderId, player, null, lister,
				ItemChangeWay.SUB_USE_ITEM, getZoneId(), "", null, ItemChangeParam.NO_EXP, this, 
				"onSubBroadcastCardToDcCallback", request,
				itemOnPlayer.getItemId());
	}
	
	/**
	 * 收到 client 要求角色身上的道具清單
	 * 
	 * @param player
	 */
	public void onGetItemListRequest(Player player) {
		if (player == null) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + ": player = null" + "");
			}
			return;
		}

		ArrayList<Item> playerItems = player.getItemBox().getItems();
		ArrayList<SSimpleItem> itemListClient = new ArrayList<SSimpleItem>();
		for (Item item : playerItems) {
			itemListClient.add(item.toClientFormat());
		}

		SZoneGetItemListResult result = new SZoneGetItemListResult();
		result.setItemList(itemListClient);
		result.setErrorCode(ErrorCode.SUCCESS);

		MessageAction.sendMessage(player.getSessionID(), MessageTypes.S_ZONE_GET_ITEM_LIST_RESULT, result);
	}
	
	/**
	 * 
	 * @param module
	 * @param player
	 * @param accountItemSn
	 */
	public void onGetItemFromAccountItemList(BasicServiceModule module, Player player, String accountItemSn) {
		
	}
	
	private void sendMessageToClientRemoveBlockList(GUID playerId, GUID sessionId, int messageType,Object content){
		removeBlockList(playerId);
		sendMessageToClient(sessionId, messageType, content);
	}
	
	/** 
	 * 去DC扣除角色身上的福袋回來
	 *  {@link BasicZone#onOpenBonusPack}
	 */
	public void onSubBonusPackBack(BaseDataInfo obj, Object... object) {
		if (obj.getResultCode() == ErrorCode.SUCCESS) {
			ItemBasic itemBasic = (ItemBasic) object[0];
			GUID playerId = (GUID) object[1];
			String itemSN = (String) object[2];
			Player player = GameUtility.getPlayerFromZone(this.getZoneId(), playerId);
			if (player == null) { // 斷線惹
				if(log.isErrorEnabled()) {
					log.error("onSubBonusPackBack Player DisConnect");
				}
				return;
			}

			ItemChangeParam itemChangeParam = (ItemChangeParam) obj.getData();
			itemBasic.use(player, null, 0, null, itemChangeParam.getOrderId(), itemSN);
		}
	}
	
	/**
	 * callback from
	 * {@link #onUseBroadcastCardRequest(Player, CUseBroadcastCardRequest)}
	 * 
	 * @param obj
	 * @param userObject
	 *            [0] CUseBroadcastCardRequest request
	 *            [1] String itemId
	 */
	public void onSubBroadcastCardToDcCallback(BaseDataInfo obj, Object... userObject) {
		ItemChangeParam param = (ItemChangeParam) obj.getData();
		Player player = GameUtility.getPlayerFromZone(param.getSrcModuleId(), param.getPlayerId());
		if (player == null) {
			return;
		}
		
		//從block list移除
		removeBlockList(player.getGuid(), null);

		// 道具扣除失敗
		if (param.getResult() != ErrorCode.SUCCESS) {
			if (log.isErrorEnabled()) {
				log.error(param.getResultString());
			}

			ItemChannelBroadcastCard.sendUseBroadcastCardResponse(player.getSessionID(), param.getResult());
			return;
		}

		// 取得一開始的全頻卡使用要求
		CUseBroadcastCardRequest request = (CUseBroadcastCardRequest) userObject[0];
		
		// 要求使用的道具 id
		String itemId = (String) userObject[1];

		// 取得全頻卡
		ItemChannelBroadcastCard broadcastCard = (ItemChannelBroadcastCard) ItemInfoManager.getInstance().getItem(itemId);

		// 廣播
		broadcastCard.sendBroadcastMessage(player.getName(), request.getMessage());
	}

	public boolean isAutoRemovePlayerOnDissconnect() {
		return autoRemovePlayerOnDissconnect;
	}

	public void setAutoRemovePlayerOnDissconnect(boolean autoRemovePlayerOnDissconnect) {
		this.autoRemovePlayerOnDissconnect = autoRemovePlayerOnDissconnect;
	}
	
	private void DebugLog(String guid)
	{
		if(!log.isDebugEnabled())
		{
			return;
		}
		
		
		log.debug("<<"
				+ Thread.currentThread().getStackTrace()[2].getMethodName()
				+ ">>   GUID : " + guid);
	}
	
	/**
	 * 產生 blocklist 用的 key
	 * 
	 * @param playerId
	 * @param subKey
	 * @return
	 */
	private String makeBlockKey(String subKey) {
		StringBuilder sb = new StringBuilder();
		sb.append("_");

		if (subKey != null) {
			sb.append(subKey);
		}

		return sb.toString();
	}
	
	/**
	 * 只針對玩家 特定key的blocklist去移除
	 * @param playerId
	 * @param subKey
	 *            see {@link #putBlockList(GUID, String, int)}
	 */
	public void removeBlockList(GUID playerId, String subKey) {
		String key = makeBlockKey(subKey);
		HashMap<String, Integer> blockList = getBlockList(playerId);
		if (blockList.containsKey(key)) {
			if (log.isInfoEnabled()) {
				log.info("remove player " + playerId + " from blockList: " + ConstantCode.getName(BlockType.class, blockList.get(key)));
			}
		}
		blockList.remove(key);
	}
	
	/**
	 * 把所有對應playerId的BlockList都移除掉
	 * @param playerId
	 */
	public void removeBlockList(GUID playerId){
		blockListMap.remove(playerId.toString());
		if (log.isInfoEnabled()) {
			log.info("remove player " + playerId + " from blockList: all");
		}
	}

	/**
	 * 標記角色正在進行某些需要等待另一個 module 或執行緒回應的事情, 這時候不能接受其他的要求.<br>
	 * 使用方式:<br>
	 * 1. 將事件對應的 id 丟進 list 中 {@link #putBlockList(GUID, String, int)}<br>
	 * 2. 收到要求的時候檢查是否有已經在進行的事件 {@link #isBlock(GUID, String)}<br>
	 * 3. 事件結束的時候將 id 移除 {@link #removeBlockList(GUID, String)}
	 * 
	 * @param playerId
	 * @param subKey
	 *            如果是標記阻擋期間可以同時進行的事件, 又要阻擋不被重複呼叫, 這裡就要填 "代表這個事件的關鍵字", 如果不需要就填
	 *            null<br>
	 *            例如處理 spin 的期間, 不能再處理其他要求, 但要可以搜尋角色的遊戲記錄, 又要阻擋重複的尋要求, 這時就要指定
	 *            subkey
	 * @param type
	 *            {@link BlockType}
	 */
	public void putBlockList(GUID playerId, String subKey, int type) {
		String key = makeBlockKey(subKey);
		HashMap<String, Integer> blockList = getBlockList(playerId);
		blockList.put(key, type);
		if (log.isInfoEnabled()) {
			log.info("add player " + playerId + " to blockList: "
					+ ConstantCode.getName(BlockType.class, blockList.get(key)));
		}
		
		blockListMap.put(playerId.toString(), blockList);
	}
	
	/**
	 * 取得blocklist
	 * @param playerId
	 * @return
	 */
	private HashMap<String, Integer> getBlockList(GUID playerId){
		
		if (playerId == null) {
			if (log.isErrorEnabled()) {
				log.error("makeBlockKey() playerId = null", new RuntimeException());
			}
		}
		
		HashMap<String, Integer> blockList = blockListMap.get(playerId.toString());
		
		return blockList == null ? new HashMap<String, Integer>() : blockList;
	}

	/**
	 * 
	 * @param playerId
	 * @param subKey
	 *            see {@link #putBlockList(GUID, String, int)}
	 * @return
	 */
	public boolean isBlock(GUID playerId, String subKey) {
		String key = makeBlockKey(subKey);
		HashMap<String, Integer> blockList = getBlockList(playerId);
		
		if (blockList.containsKey(key)) {
			if (log.isInfoEnabled()) {
				log.info("player " + playerId + " is busy, in block list: "
						+ ConstantCode.getName(BlockType.class, blockList.get(key)));
			}
			return true;
		}
		return false;
	}
	
	public int getBlockType(GUID playerId, String subKey) {
		String key = makeBlockKey(subKey);
		HashMap<String, Integer> blockList = getBlockList(playerId);
		if (blockList.containsKey(key)) {
			return blockList.get(key);
		}

		return BlockType.NONE;
	}

	/**
	 * 轉點 
	 */
	public void changePointToGold(GUID sessionID , BankRequest request , BasicZoneModule zoneModule){
		GUID playerID = PlayerCommonInfoManager.getInstance().getPlayerGuidBySession(sessionID);
		boolean hasPlayer = false;
		
		BankResponse response = new BankResponse();
		
		if(zoneModule.isStopTransaction()){						
			response.errorCode = ErrorCode.GAME_SERVER_WAIT_TO_CLOSE;					
			MessageAction.sendMessage(sessionID, MessageTypes.S_BANK_CHANGE_POINT_TO_GOLD_RESPONSE, response);					
			return;					
		}						
		
		if(playerID != null){
			hasPlayer = playerList.containsKey(playerID);
		}
		//玩家不存在
		if(!hasPlayer){
			response.errorCode = ErrorCode.NO_PLAYER_DATA;
			MessageAction.sendMessage(sessionID, MessageTypes.S_BANK_CHANGE_POINT_TO_GOLD_RESPONSE, response);
			return;
		}
		
		if(isBlock(playerID,null)){
			//跟client說目前無法轉點
			response.errorCode = ErrorCode.CANT_CHANGE_POINT_TO_GOLD;
			MessageAction.sendMessage(sessionID, MessageTypes.S_BANK_CHANGE_POINT_TO_GOLD_RESPONSE, response);
			return;
		}else{
			putBlockList(playerID, null, BlockType.CHANGE_POINT_TO_GOLD);
			BankProcessor.OnChangePointToGoldCoin(sessionID, request, zoneModule);
		}
		
	}
	
	public void changePointToGoldCallback(GUID sessionID , BankResponse response ,GUID playerID){
		if(isBlock(playerID, null)){			
			removeBlockList(playerID, null);
		}
		MessageAction.sendMessage(sessionID, MessageTypes.S_BANK_CHANGE_POINT_TO_GOLD_RESPONSE, response);
	}
	
	public void onRobotAddCash(GUID sessionID, BasicZoneModule zoneModule, Player playerData, long cashChangeValue) {
		SRobotAddCashMessage msg = new SRobotAddCashMessage();
		int messageType = MessageTypes.S_ZONE_ROBOT_ADD_CASH_POINT_RESPONSE;
		if (playerData == null) {
			msg.setErrorCode(ErrorCode.PLAYER_NOT_FOUND);
			MessageAction.sendMessage(sessionID, messageType, msg);
			return;
		}
		
		//這個api 只有機器人能用
		if(playerData.getUserType() != PlayerUserType.ROBORT){
			return;
		}
		
		GUID playerID =  playerData.getGuid();
		RobotAddCashPointAsync async = new RobotAddCashPointAsync();
		async.key = playerID;
		async.cashChangeValue = cashChangeValue;
		async.accountID = playerData.getAccountId();
		async.playerID = playerID;
		async.playerName = PlayerNickNameParser.mergeNickName(playerData.getName(), playerData.getNameNumber());
		async.sessionID = sessionID;
		async.messageType = messageType;
		zoneModule.addCentralAsyncCommand(async,zoneModule);
	}
	
	/**
	 * 小樂要求增加體驗幣
	 * 
	 * @param player
	 */
	public void onRobotAddTrialCoin(Player player) {
		GUID orderId = GUIDFactory.createUUID(GUIDType.TYPE_TRIAL_COIN);

		ChangeCoinLister coinLister = new ChangeCoinLister();
		coinLister.addCoin(CoinType.TRIAL, new BigDecimal(100000));

		MoneyChangeWay changeWay = MoneyChangeWay.ADD_ROBOT_ADD_TRIAL;

		BigDecimal jpMoney = BigDecimal.ZERO;

		boolean isOfflineBet = false;

		ArrayList<ItemSimpleData> rollItemsInGame = null;

		String gameId = "";

		String gameName = "";

		String gameHallId = "";

		String gameHallName = "";

		Object classOfCallback = this;

		String callback = "onRobotAddTrialCoinCallback";

		getOwnerModule().getPlayerDataAgentZone().updatePlayerMoneyToDC(orderId, player, coinLister, changeWay,
				jpMoney, isOfflineBet, rollItemsInGame, null, getOwnerModule(), gameId, gameName, gameHallId,
				gameHallName, null, classOfCallback, callback);
	}
	
	/**
	 * 小樂要求增加體驗幣的 callback
	 * 
	 * @param obj
	 * @param userObject
	 */
	public void onRobotAddTrialCoinCallback(BaseDataInfo obj, Object... userObject) {
		MoneyChangeParam param = (MoneyChangeParam) obj.getData();
		Player player = getPlayer(param.getPlayerId());
		if (player == null) {
			return;
		}

		// 處理完通知 client
		MessageAction.sendMessage(player.getSessionID(), MessageTypes.S_ZONE_ROBOT_ADD_TRIAL_COIN_RESULT, null);
	}

	
	/**
	 * 取得連續獎勵資訊
	 */
	public void getContinueRewardInfo(Player player , GUID sessionId){
		
		//檢查每日登入獎勵
		ContinuiusRewardInfo rewardInfo = checkContinueRewardInfo(player);
		
		//發給client每日獎勵的消息
		MessageAction.sendMessage(sessionId, MessageTypes.S_CONTINUE_REWARD, rewardInfo);
	}
	
	/**
	 * 檢查每日獎勵資訊
	 */
	public ContinuiusRewardInfo checkContinueRewardInfo(Player player){
		ContinuiusRewardInfo rewardInfo = new ContinuiusRewardInfo();
		//設定已領取過數量
		rewardInfo.setPurchaseRewardIsReceive(player.getFirstStoreReward());
		
		
		//檢查領獎資格
		if(player.getStoreRewardContinuousLoginCount() == 0){
			rewardInfo.setPurchaseReward(0);
			return rewardInfo;
		}
		
		//讀bin檔取出可領總數
		int rewardLimit = ContinuousLoginRewardBin.getInstance().getDataMap().size();
		
		//當前可領取的獎勵上限(比照連續登錄次數，但最大不超過5)
		int maxReward = (player.getStoreRewardContinuousLoginCount() > rewardLimit)? rewardLimit : player.getStoreRewardContinuousLoginCount();
		int firstStoreRewardNeed = 0;	//儲值獎勵可領數量
		//判斷是否可領儲值獎勵
		if(player.isPurchaseMember() && player.getFirstStoreReward() < maxReward){  
			//可領取數量 = 當前可領取上限 - 已領取數量
			firstStoreRewardNeed = maxReward - player.getFirstStoreReward();
		}
		rewardInfo.setPurchaseReward(firstStoreRewardNeed);
		
		return rewardInfo;
	}
	
	/**
	 * 發送連續登陸獎勵
	 */
	public void sendContinueRewardMessage(int needReward , Player player , GUID sessionId){
		int receiveCount = 0;
		
		
		BasicZoneModule module = (BasicZoneModule)Contexts.getModule();
		
		if(module.isStopTransaction()){
			MessageAction.sendMessage(sessionId, MessageTypes.S_GET_CONTINUE_REWARD, ErrorCode.GAME_SERVER_WAIT_TO_CLOSE);
			return;
		}
		
		GUID playerID = PlayerCommonInfoManager.getInstance().getPlayerGuidBySession(sessionId);
		//如果正在做其他事情就回傳不行領獎勵
		if(isBlock(playerID, null)){
			MessageAction.sendMessage(sessionId, MessageTypes.S_GET_CONTINUE_REWARD, ErrorCode.CANT_GET_CONTINUE_REWARD_NOW);
			return;
		}else{
			//記錄正在領獎
			putBlockList(playerID, null, BlockType.GET_CONTINUE_REWARD);
		}
		
		ContinuiusRewardInfo rewardInfo = checkContinueRewardInfo(player);
		
		//檢查是否有可領獎勵 & 要領取的獎勵是否在範圍內 ( 要領的天數(第幾天)要小於 (已領天數+可領天數) )
		if(rewardInfo.getPurchaseReward() > 0 && needReward <= (rewardInfo.getPurchaseReward() + rewardInfo.getPurchaseRewardIsReceive())){
			
			ArrayList<UpdateAttribute> updatedAttrList = new ArrayList<UpdateAttribute>(); //要更新的屬性清單
			
			UpdateAttribute attr = new UpdateAttribute();
			attr.setId(PlayerAttributeType.PURCHASE_REWARD_COUNT);
			
			receiveCount = rewardInfo.getPurchaseRewardIsReceive() + 1;
			player.setFirstStoreReward(receiveCount);
			attr.setValue(receiveCount + "");
			updatedAttrList.add(attr);
			//每次只領一個，所以直接加1
//			player.setFirstStoreReward( rewardInfo.getPurchaseReward() + 1 );
			
			ChangeCoinLister coinLister = new ChangeCoinLister();
			ChangeItemLister itemLister = new ChangeItemLister();
			
			
			setContinueRewardToPlayer(rewardInfo,coinLister,itemLister,needReward);
			
			GUID orderId = GUIDFactory.createUUID(GUIDType.CONTINUE_REWARD);
			
			//送到統計C幣支出的值
			BigDecimal addValue = BigDecimal.ZERO;
			if(coinLister != null && coinLister.getCoinMap().size() > 0 && coinLister.getCoinMap().containsKey(CoinType.GOLD)){
				addValue = coinLister.getCoinMap().get(CoinType.GOLD).getChangeValue();
			}
			
			module.getPlayerDataAgent().updatePlayerMoneyAndItemToDC(orderId, player, 
					coinLister, MoneyChangeWay.ADD_CONTINUE_REWARD,
					itemLister, ItemChangeWay.ADD_CONTINUE_REWARD,
					updatedAttrList, null, null, this , "sendContinueRewardMessageToClient",
					player.getSessionID(),playerID.toString(),receiveCount,addValue);
		}else{
			sendContinueRewardMessageToClient(new BaseDataInfo(),player.getSessionID(),playerID.toString(),receiveCount);
		}
	}
	
	public void sendContinueRewardMessageToClient(BaseDataInfo obj , Object... userObject){
		GUID sessionId = (GUID)userObject[0];
		String playerId = (String)userObject[1];
		boolean result = (obj.getResultCode() == 1)? true : false;
		int receiveCount = (int)userObject[2];
		removeBlockList(GUID.parseUUID(playerId), null);
		//發給client每日獎勵的消息
		MessageAction.sendMessage(sessionId, MessageTypes.S_GET_CONTINUE_REWARD, result ? ErrorCode.SUCCESS : ErrorCode.NULL);
		
		//送到統計C幣支出
		if(result){
			BigDecimal addValue = (BigDecimal)userObject[3];
			if(Helper.isGreaterThanZero(addValue))
				AnalysisGroupProcessor.sendCoinExpenditureToAnalysis(CoinExpenditureWay.DEPOSIT_REWARD, addValue);
		}
//		if(result){
//			//首次加到waitingMap
//			RedPointProcessor.AddToWaitingMap(GUID.parseUUID(playerId), RedPointNotifyTypes.FirstStoreReward, receiveCount);
//		}
	}
	
	/**
	 * 設定連續登陸獎勵 
	 */
	public void setContinueRewardToPlayer(ContinuiusRewardInfo rewardInfo,
			ChangeCoinLister coinLister , ChangeItemLister itemLister , int needReward){
		HashMap<String, ContinuousLoginReward> rewardDataMap = ContinuousLoginRewardBin.getInstance().getDataMap();
		
		int purchaseReward = rewardInfo.getPurchaseReward();
		int purchaseRewardIsReceive = rewardInfo.getPurchaseRewardIsReceive();
		
		if(rewardDataMap.containsKey(needReward)){
			ContinuousLoginReward reward = rewardDataMap.get(needReward);
			//取額儲值獎勵內容
			ContinuousLoginRewardDetail rewardDetial = reward.getPurchaseMemberReward();
			String coin = rewardDetial.getGoldCoin();
			if (GameUtility.isHaveString(coin)) {
				coinLister.addCoin(CoinType.GOLD, new BigDecimal(coin));
			}

			ArrayList<ItemInfo> itemList = rewardDetial.getItemList();
			if (itemList != null) {
				for (ItemInfo info : itemList) {
					String itemId = info.getItemId();
					ItemBasic itemBasic = ItemInfoManager.getInstance().getItem(itemId);
					itemLister.addItem(null, itemId, info.getCount(), itemBasic.getRefineExpireTime());
				}
			}
		}
	}
	
	/**
	 * 更新玩家禁止送禮的時間跟理由
	 * */
	public boolean updatePlayerDisableSendGiftTimeReason(PlayerDisableData data) {
		GUID id = data.getGuid();
		long time = data.getDisableSendGiftTime();
		String reason = data.getDisableSendGiftReason();
		if(playerList.containsKey(id))
		{
			Player player = playerList.get(id);
			player.setDisableSendGiftTime(time);
			player.setDisableSendGiftReason(reason);
			playerList.put(id, player);
			if (log.isInfoEnabled()) 
			{
				log.info("ID : " + id + ", setDisableSendGiftTime : " + time + ", setDisableSendGiftReason : " + reason);
			}
			return true;
		}
		return false;
	
	}
	
	public void onOpenRechargePage(GUID sessionID, BasicZoneModule module){
		
		GUID playerID = PlayerCommonInfoManager.getInstance().getPlayerGuidBySession(sessionID);
		Player playerData =null;
		if(playerID != null){
				playerData = getPlayer(playerID);
		}
		if(playerData == null){
			SRechargePageResponse response = new SRechargePageResponse();
			response.errorCode = ErrorCode.NO_PLAYER_DATA;
			MessageAction.sendMessage(sessionID, MessageTypes.S_BANK_INFO_RESPONSE, response);
			return;
		}

		GUID accountID = PlayerCommonInfoManager.getInstance().getAccountIdByPlayerGuid(playerID);
		
		LoadPlayerCashItemDCLAsync async = new LoadPlayerCashItemDCLAsync();
		async.sessionID = sessionID;
		async.accountID = accountID;
		async.monthlyFeeEndTime = playerData.getMonthlyFeeEndTime();
		async.weeklyFeeEndTime = playerData.getWeeklyFeeEndTime();
		module.addCentralAsyncCommand(async);
	}

	public void getCashGoldTransactionDetais(GUID sessionID, BasicZoneModule module, CCashGoldTransactionDetailsRequest request) {
		GUID playerID = PlayerCommonInfoManager.getInstance().getPlayerGuidBySession(sessionID);
		
		int messageType = MessageTypes.S_ZONE_CASH_GOLD_TRANSACTION_DETAIL_RESPONSE;
		
		Player playerData = null;
		if (playerID != null) {
			playerData = getPlayer(playerID);
		}
		if (playerData == null) {
			SCashGoldTransactionDetailsResponse response = new SCashGoldTransactionDetailsResponse();
			response.errorCode = ErrorCode.NO_PLAYER_DATA;
			MessageAction.sendMessage(sessionID, messageType, response);
			return;
		}
		GUID accountId = PlayerCommonInfoManager.getInstance().getAccountIdByPlayerGuid(playerID);
		if(accountId == null){
			SCashGoldTransactionDetailsResponse response = new SCashGoldTransactionDetailsResponse();
			response.errorCode = ErrorCode.NO_ACCOUNT_DATA;
			MessageAction.sendMessage(sessionID, MessageTypes.S_ZONE_CASH_GOLD_TRANSACTION_DETAIL_RESPONSE, response);
			return;
		}
		
		LoadCashGoldTransactionDetailsDCLAsync async = new LoadCashGoldTransactionDetailsDCLAsync();
		async.playerID = playerID;
		async.key = playerID;
		async.messageType = messageType;
		async.startTime = request.getStartTime();
		async.endTime = request.getEndTime();
		async.sessionID = sessionID;
		async.accountID = accountId;
		module.addAsyncCommand(async);
	}

	/**
	 * 收到精煉道具的請求
	 * 
	 * @param player
	 * @param refineItemSn
	 *            要精煉的道具序號
	 */
	public void recvItemRefineRequest(Player player, String refineItemSn) {
		if (player == null) {
			return;
		}
		
		BasicZoneModule module = (BasicZoneModule)Contexts.getModule();
		if(module.isStopTransaction()){						
			MessageAction.sendFailResultClientMessage(player.getSessionID(), MessageTypes.S_ITEM_REFINE_RESULT,
					SItemRefineResult.class, ErrorCode.GAME_SERVER_WAIT_TO_CLOSE);			
			return;					
		}			
		
		int limitCode = GameLimitInfo.checkLimitByClassify(GameLimitInfo.ITEM_LVUPGRADE, player.getVip(),player.getLevel(), player.isVerifiedMember(), player.isTrialAccount(), player.getUplineCode());
		if(limitCode != GameLimitInfo.TRUE){
			int errorCode = GameLimitInfo.limitToErrorCode(limitCode);
			MessageAction.sendFailResultClientMessage(player.getSessionID(), MessageTypes.S_ITEM_REFINE_RESULT,
					SItemRefineResult.class, errorCode);
			return;
		}		

		if (!GameUtility.isHaveString(refineItemSn)) {
			MessageAction.sendFailResultClientMessage(player.getSessionID(), MessageTypes.S_ITEM_REFINE_RESULT,
					SItemRefineResult.class, ErrorCode.INVALID_PARAM);
			return;
		}

		// 取得角色身上的道具
		Item item = player.getItemBox().getItem(refineItemSn);
		if (item == null) {
			MessageAction.sendFailResultClientMessage(player.getSessionID(), MessageTypes.S_ITEM_REFINE_RESULT,
					SItemRefineResult.class, ErrorCode.ITEM_NOT_FOUND_FROM_PLAYER);
			return;
		}

		// 做相關檢查
		int resultCode = ItemRefineDCLAsync.refineItemVerify(item, player.getCrystal());
		if (resultCode != ErrorCode.SUCCESS) {
			MessageAction.sendFailResultClientMessage(player.getSessionID(), MessageTypes.S_ITEM_REFINE_RESULT,
					SItemRefineResult.class, resultCode);
			return;
		}

		// 丟到 dc 去執行
		ItemRefineDCLAsync task = new ItemRefineDCLAsync();
		task.setSrcModuleId(getZoneId());
		task.setPlayerId(player.getGuid());
		task.setItemSn(item.getSn());
		task.key = player.getGuid();
		
		getOwnerModule().addCentralAsyncCommandToDC(task, player);
	}
	
	/**
	 * client 要求付費相關項目資料
	 * 
	 * @param player
	 */
	public void recvPurchaseDataListRequest(Player player) {
		if (player == null) {
			return;
		}

		// 是否為正式 server
		boolean isFormalServer = ServerProperties.getBoolean(PropertyKey.formal_server, false);

		// 將付費資料填入回傳容器中
		ArrayList<PurchaseItemInfo> list = new ArrayList<PurchaseItemInfo>();
		for (PurchaseItemInfo pInfo : PurchaseData.getInstance().getPurchaseItemList()) {
			if (pInfo.isFormalData() == isFormalServer) {
				list.add(pInfo);
			}
		}

		// 回傳結果
		SPurchaseDataListResult result = new SPurchaseDataListResult();
		result.setPurchaseDataList(list);

		MessageAction.sendMessage(player.getSessionID(), MessageTypes.S_PURCHASE_DATA_LIST_RESULT, result);
	}
	
	
	/**
	 * client 要求付費相關項目資料(iOS)
	 * 
	 * @param player
	 */
	public void recvPurchaseDataListRequest_IOS(Player player) {
		if (player == null) {
			return;
		}

		// 是否為正式 server
		boolean isFormalServer = ServerProperties.getBoolean(PropertyKey.formal_server, false);

		// 將付費資料填入回傳容器中
		ArrayList<PurchaseItemInfoIOS> list = new ArrayList<PurchaseItemInfoIOS>();
		for (PurchaseItemInfoIOS pInfo : PurchaseData.getInstance().getPurchaseItemList_IOS()) {
			if (pInfo.isFormalData() == isFormalServer) {
				list.add(pInfo);
			}
		}

		// 回傳結果
		SPurchaseDataListResult_IOS result = new SPurchaseDataListResult_IOS();
		result.setPurchaseDataList(list);

		MessageAction.sendMessage(player.getSessionID(), MessageTypes.S_PURCHASE_DATA_LIST_RESULT_IOS, result);
	}
	
	/**
	 * 儲存離線 spin 結果資料
	 * 
	 * @param playerId
	 * @param result
	 *            離線 spin 結果資料
	 */
	public void saveOfflineSpinResult(GUID playerId, OfflineSpinResultData result) {
		if (playerId == null) {
			return;
		}

		if (result == null) {
			return;
		}

		SaveOfflineSpinResultAsync task = new SaveOfflineSpinResultAsync();
		task.setPlayerId(playerId);
		task.setResult(result);
		task.key = playerId;

		getOwnerModule().addAsyncCommand(task);
	}
	
	/**
	 * 因某些原因停止離線spin而修改離線spin結果
	 * @param playerId
	 */
	public void saveOfflineSpinResultByCondition(GUID playerId, OfflineSpinStopCondition condition){
		if(playerId == null)
			return;
		
		SaveOfflineSpinStopResultAsync async = new SaveOfflineSpinStopResultAsync();
		async.setPlayerGUID(playerId);
		async.setCondition(condition);
		
		getOwnerModule().addAsyncCommand(async);
	}

	public void onIapApplyOrderID(GUID sessionID,CApplyIAPOrderIDRequest request) {
		GroupMessage msg = new GroupMessage();
		msg.setSessionID(sessionID);
		msg.setMessageContent(request);
		msg.setDestModuleID(ModuleName.GMTOOL);
		msg.setMessageType(MessageTypes.S_APPLY_IAP_ORDERID_GMTOOL);
		MessageAction.sendGroupMessage(msg);
	}

	public void onIapBillingVerify(GUID sessionID, CApplyIAPVerifyRequest request) {
		GroupMessage msg = new GroupMessage();
		msg.setMessageContent(request);
		msg.setSessionID(sessionID);
		msg.setDestModuleID(ModuleName.GMTOOL);
		msg.setMessageType(MessageTypes.S_VERIFY_IAP_DATA_GMTOOL);
		MessageAction.sendGroupMessage(msg);
		writeIapLog(GameUtility.getCurMethodName(), request);
	}
	
	private void writeIapLog(String methodName, CApplyIAPVerifyRequest request){
		if(log.isInfoEnabled()){
			log.info(methodName + " | orderId = " + request.getOrderID() + 
					" | iapOrderId = " + request.getIapOrderId() + 
					" | purchaseInfo = " + request.getPurchaseInfo() + 
					" | signature = " + request.getSignature() +
					" | verify = " + request.getVerify());
		}
	}
	
	public void onIapAskGiveCash(GUID sessionID, CApplyIAPVerifyRequest request){
		GroupMessage msg = new GroupMessage();
		msg.setMessageContent(request);
		msg.setSessionID(sessionID);
		msg.setDestModuleID(ModuleName.GMTOOL);
		msg.setMessageType(MessageTypes.S_ASK_GIVE_IAP_CASH_GMTOOL);
		MessageAction.sendGroupMessage(msg);
		writeIapLog(GameUtility.getCurMethodName(), request);
	}
	
	public void onAppleIapVerify(GUID sessionID, CAppleIapVerifyRequest request){
		GroupMessage msg = new GroupMessage();
		msg.setMessageContent(request);
		msg.setSessionID(sessionID);
		msg.setDestModuleID(ModuleName.GMTOOL);
		msg.setMessageType(MessageTypes.S_APPLE_IAP_VERIFY_GMTOOL);
		MessageAction.sendGroupMessage(msg);
		
		if(log.isInfoEnabled()){
			log.info(GameUtility.getCurMethodName() + 
				" | orderId = " + request.getOrderId() +
				" | verify = " + request.getVerify() +	
				" | receipt = " + request.getReceipt());
		}
		
	}
	
	public void onAppleAskCash(GUID sessionID, CAppleIapAskCashRequest request){
		GroupMessage msg = new GroupMessage();
		msg.setMessageContent(request);
		msg.setSessionID(sessionID);
		msg.setDestModuleID(ModuleName.GMTOOL);
		msg.setMessageType(MessageTypes.S_APPLE_IAP_ASK_CASH_GMTOOL);
		MessageAction.sendGroupMessage(msg);
		
		if(log.isInfoEnabled()){
			log.info(GameUtility.getCurMethodName() + 
					" | orderId " + request.getOrderId() + 
					" | verify " + request.getVerify() +
					" | iapOrderId " + request.getIapOrderId());
		}
	}
	
	/**
	 * 角色登入遊戲中的通知
	 * 
	 * @param notify
	 */
	public void onPlayerLoginGameNotify(SCharacterLoginNotify notify) {
	}

	/**
	 * 清除 zone 上 LoginPlayerList 內指定的 guid
	 * 
	 * @param playerId
	 */
	public void removeGuidFromLoginPlayerList(GUID playerId) {
		if (playerId == null) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + ": playerId = null");
			}
			return;
		}

		if (!loginPlayerList.containsKey(playerId)) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + ": playerId " + playerId
						+ " not found in LoginPlayerList");
			}
			return;
		}

		loginPlayerList.remove(playerId);

		if (log.isInfoEnabled()) {
			log.info("remove guid " + playerId + " from LoginPlayerList");
		}
	}
	
	/**
	 * 傳送資料給server
	 * 
	 * @param messagetype
	 * @param response
	 * @param sessionid
	 */
	public void sendMessageToServer(GUID sessionid, String srcMID,
			int messagetype, Object response) {
		GroupMessage msg = new GroupMessage();
		msg.setMessageType(messagetype);
		msg.setMessageContent(response);
		msg.setSessionID(sessionid);
		msg.setDestModuleID(srcMID);
		MessageAction.sendGroupMessage(msg);
	}
	
	/**
	 * 取得account的opID
	 */
	public void getAccountOPIDandResonse(GUID accountID, GUID sessionID){
		//丟給執行續去回答
		GetAccountOPIDAsync async = new GetAccountOPIDAsync();
		async.setAccountID(accountID);
		async.setSessionID(sessionID);
		getOwnerModule().addAsyncCommand(async);
	}
	
	/**
	 * 送下注的任務判斷
	 */
	public void sendMissionCheckBet(GameType gameType, SlotBetInfo betInfo){
		if(betInfo == null){
			if(log.isErrorEnabled()){
				log.error("betInfo is null at sendMissionCheckBet");
			}
			return;
		}
		
		Player player = betInfo.player;
		if(player == null){
			if(log.isErrorEnabled()){
				log.error("player is null at sendMissionCheckBet");
			}
			return;
		}
		
		if(!player.isVerifiedMember() || player.isUseTrialCoin() || player.getUserType() != PlayerUserType.PLAYER)
			return;
		
		String gameTypeStr = gameType.name();
		String hallType = betInfo.halltype;
		if(!GainRateSystem.isCanDoMission(gameTypeStr, hallType, betInfo.gainRate)){
			return;
		}
	}
	
	/**
	 * 儲存骰物品結果
	 */
	public void saveRollItemResult(GameType gameType, int machineID, SlotBetInfo betInfo, GUID orderID, ArrayList<ItemSimpleData> rollItemsInGame){
		if(rollItemsInGame != null && betInfo != null && gameType != null){
			
			if(rollItemsInGame.size() == 0)
				return;
			
			Player player = betInfo.player;
			if(player == null){
				if(log.isErrorEnabled()){
					log.error("player in betInfo is null!!!");
				}
				return;
			}
			
			for(ItemSimpleData itemData : rollItemsInGame){
				RollItemInGameLogModel rollItemLog = new RollItemInGameLogModel();
				rollItemLog.setBet(betInfo.getRealBetMoney());
				rollItemLog.setGameType(gameType);
				rollItemLog.setHallType(betInfo.halltype);
				rollItemLog.setMachineID(machineID);
				rollItemLog.setPlayerGUID(player.getGuid().toString());
				rollItemLog.setPlayerName(player.getNameWithNumber());
				rollItemLog.setOrderID(orderID);
				rollItemLog.setUserType(player.getUserType());
				rollItemLog.setItemID(itemData.getItemid());
				rollItemLog.setCount(itemData.getCount());
				
				getOwnerModule().saveDbLog(rollItemLog);								
			}
		}
	}
	
	/**
	 * 收到Client詢問是否有送東西
	 */
	public void onCheckDailySend(Player player, GUID sessionId, BasicZoneModule module){
		
		DailyLoginSendResponse response = new DailyLoginSendResponse();
		if(!player.isFormalMember()){
			response.setErrorCode(ErrorCode.SUCCESS);
			MessageAction.sendMessage(sessionId, MessageTypes.S_ZONE_CHECK_DAILY_SEND_RESPONSE, response);
			return;
		}
		
		
		CommonSettings commonSetting = module.getCommonSettings();
		if(commonSetting == null || commonSetting.getSendItemSetting() == null){
			response.setErrorCode(ErrorCode.SUCCESS);
			MessageAction.sendMessage(sessionId, MessageTypes.S_ZONE_CHECK_DAILY_SEND_RESPONSE, response);
			return;
		}
		
		CommonSendItemSetting sendItemSetting = commonSetting.getSendItemSetting();
		if(sendItemSetting == null || sendItemSetting.getDailySendSettings() == null){
			response.setErrorCode(ErrorCode.SUCCESS);
			MessageAction.sendMessage(sessionId, MessageTypes.S_ZONE_CHECK_DAILY_SEND_RESPONSE, response);
			return;
		}
		
		DailyLoginSendSettings dailyLoginSendSetting = sendItemSetting.getDailySendSettings();
		if (!dailyLoginSendSetting.isOpen()) {
			response.setErrorCode(ErrorCode.SUCCESS);
			MessageAction.sendMessage(sessionId, MessageTypes.S_ZONE_CHECK_DAILY_SEND_RESPONSE, response);
			return;
		}
		
		ArrayList<DailySendItemInfo> dailySendItemInfos = dailyLoginSendSetting.getItems();
			
		// 送去async檢查有沒有翻寶券可以領
		CheckDailyLoginSendItemAsync async = new CheckDailyLoginSendItemAsync();
		async.key = player.getAccountId();
		async.setPlayer(player);
		async.setDailySendItemInfos(dailySendItemInfos);
		async.setModuleServer(getOwnerModule());
		async.setSessionId(getBaseaction().getSessionID());
		addTaskToModule(async);
		return;
	}

	/**
	 * 收到要求把暫存錢包的錢放到 player 上
	 * 
	 * @param player
	 * @param request
	 */
	public void recvTmpCashTakeRequest(Player player, CTmpCashTakeRequest request) {
		if (player == null) {
			return;
		}

		// 回傳結果的消息編號
		int resultMsgType = MessageTypes.S_TMP_CASH_TAKE_RESULT;

		// 檢查要取出的金額
		if (Helper.isLess(request.getTakeCash(), BigDecimal.ZERO)) {
			MessageAction.sendFailResultClientMessage(player.getSessionID(), resultMsgType,
					STmpCashTakeResult.class, ErrorCode.TMP_CASH_INVALID_TAKE_CASH_VALUE);

			if (log.isErrorEnabled()) {
				GameUtility.failErrorLog(log, "invalid take cash value " + request.getTakeCash(),
						GameUtility.getCurMethodName(), player.getGuid(), player.getCooperatorAccountId(),
						player.getSessionID());
			}
			return;
		}

		// 鎖定資料
		if (!AccountDAO.dataLock(player.getCooperatorAccountId())) {
			// 鎖定失敗
			MessageAction.sendFailResultClientMessage(player.getSessionID(), resultMsgType,
					STmpCashTakeResult.class, ErrorCode.DATA_LOCK_FAIL);

			if (log.isErrorEnabled()) {
				GameUtility.failErrorLog(log, "lock data fail.", GameUtility.getCurMethodName(), player.getGuid(),
						player.getCooperatorAccountId(), player.getSessionID());
			}
			return;
		}

		// 取得帳號資訊
		Map<String, String> coopInfoMap = AccountDAO.getCoopInfo(player.getCooperatorAccountId());

		// 取出目前暫存錢包的金額
		String curTmpCashStr = coopInfoMap.get(CooperatorInfoField.TMP_CASH);

		BigDecimal curTmpCash = BigDecimal.ZERO;
		if (GameUtility.isHaveString(curTmpCashStr)) {
			curTmpCash = new BigDecimal(curTmpCashStr);
		}

		// 暫存錢包沒錢
		if (Helper.isEqual(curTmpCash, BigDecimal.ZERO)) {
			MessageAction.sendFailResultClientMessage(player.getSessionID(), resultMsgType,
					STmpCashTakeResult.class, ErrorCode.TMP_CASH_TMP_CASH_IS_ZERO);

			if (log.isErrorEnabled()) {
				GameUtility.failErrorLog(log, "tmp cash=0", GameUtility.getCurMethodName(), player.getGuid(),
						player.getCooperatorAccountId(), player.getSessionID());
			}
			AccountDAO.dataUnlock(player.getCooperatorAccountId());
			return;
		}

		// 是否要取出全部
		boolean takeAll = Helper.isEqual(request.getTakeCash(), BigDecimal.ZERO);

		// 要取出的金額
		BigDecimal takeCash = request.getTakeCash();
		if (takeAll) {
			takeCash = curTmpCash;
		}

		// 要取出的金額超過暫存錢包的金額
		if (Helper.isGreater(takeCash, curTmpCash)) {
			MessageAction.sendFailResultClientMessage(player.getSessionID(), resultMsgType,
					STmpCashTakeResult.class, ErrorCode.TMP_CASH_TMP_CASH_NOT_ENOUGH);

			if (log.isErrorEnabled()) {
				GameUtility.failErrorLog(log, "tmp cash not enough, tmpcash=" + curTmpCashStr + " takeCash="
						+ takeCash, GameUtility.getCurMethodName(), player.getGuid(),
						player.getCooperatorAccountId(), player.getSessionID());
			}
			AccountDAO.dataUnlock(player.getCooperatorAccountId());
			return;
		}

		// 玩家身上的金額
		BigDecimal curPlayerGold = player.getGoldCoin();

		// 暫存扣掉
		BigDecimal newTmpCash = Helper.sub(curTmpCash, takeCash);

		// 加到玩家身上
		BigDecimal newPlayerGold = Helper.add(curPlayerGold, takeCash);

		// 儲存暫存錢包到 redis
		boolean res = AccountDAO.saveCoopInfoField(player.getCooperatorAccountId(), CooperatorInfoField.TMP_CASH,
				newTmpCash.toString());
		if (!res) {
			MessageAction.sendFailResultClientMessage(player.getSessionID(), resultMsgType,
					STmpCashTakeResult.class, ErrorCode.TMP_CASH_SAVE_TMP_CASH_FAIL);

			if (log.isErrorEnabled()) {
				GameUtility.failErrorLog(log, "save tmp cash fail. newTmpCash=" + newTmpCash + " playerGold="
						+ newPlayerGold, GameUtility.getCurMethodName(), player.getGuid(),
						player.getCooperatorAccountId(), player.getSessionID());
			}
			AccountDAO.dataUnlock(player.getCooperatorAccountId());
			return;
		}

		// 儲存到玩家身上
		player.setgoldCoin(newPlayerGold);

		// player 寫回 redis
		res = PlayerDAO.savePlayerToRedis(player);
		if (!res) {
			MessageAction.sendFailResultClientMessage(player.getSessionID(), resultMsgType,
					STmpCashTakeResult.class, ErrorCode.TMP_CASH_SAVE_PLAYER_FAIL);

			if (log.isErrorEnabled()) {
				GameUtility.failErrorLog(log, "save player fail. newTmpCash=" + newTmpCash + " playerGold="
						+ newPlayerGold, GameUtility.getCurMethodName(), player.getGuid(),
						player.getCooperatorAccountId(), player.getSessionID());
			}
			AccountDAO.dataUnlock(player.getCooperatorAccountId());
			return;
		}

		// 解鎖資料
		AccountDAO.dataUnlock(player.getCooperatorAccountId());

		// 回傳更新項目給 client
		player.sendUpdatedAttrToClient();

		// 回傳結果給 client
		STmpCashTakeResult result = new STmpCashTakeResult();
		result.setNewTmpCash(newTmpCash);
		result.setNewPlayerGold(newPlayerGold);
		result.setErrorCode(ErrorCode.SUCCESS);

		MessageAction.sendMessage(player.getSessionID(), resultMsgType, result);
		
		// TODO kevin save tmp cash log

		// 寫暫存錢包 log
		GUID orderId = GUIDFactory.createUUID(GUIDType.TYPE_RECHARGE);
		TmpCashChangeWay tmpCashChangeWay = TmpCashChangeWay.SUB_TO_PLAYER_IN_GAME;

		BasicLogDAO.saveTmpCashChangeLog(
				orderId,
				coopInfoMap.get(CooperatorInfoField.ACCOUNT_ID),
				coopInfoMap.get(CooperatorInfoField.PLAYER_ID),
				coopInfoMap.get(CooperatorInfoField.OPEN_ID),
				coopInfoMap.get(CooperatorInfoField.PLAYER_NAME),
				getOwnerModule().getModuleName(),
				getOwnerModule().getModuleName(),
				player.getAgentDataInstance().getCoinType(),
				takeCash,
				curTmpCash,
				newTmpCash,
				takeCash,
				curPlayerGold,
				newPlayerGold,
				tmpCashChangeWay,
				coopInfoMap.get(CooperatorInfoField.VENDER_ID),
				coopInfoMap.get(CooperatorInfoField.SUB_VENDER_ID),
				getOwnerModule()
		);

		// 寫 money change log
		BasicLogDAO.savePlayerMoneyChangeLog(
				player, 
				getOwnerModule().getModuleName(), 
				getOwnerModule().getModuleName(), 
				getOwnerModule().getModuleName(), 
				getOwnerModule().getModuleName(), 
				MoneyChangeWay.ADD_H5_FROM_TMP_CASH_ON_MANUAL_REFRESH, 
				orderId, 
				takeCash, 
				curPlayerGold, 
				newPlayerGold, 
				getOwnerModule()
		);
		
		// 錢更新回 db
		UpdateH5PlayerMoneyToDbAsync async =  new UpdateH5PlayerMoneyToDbAsync();
		async.initial(player.getGuid(), newPlayerGold, newTmpCash);
		
		getOwnerModule().addAsyncCommand(async);
	}
}
